﻿// SciTE - Scintilla based Text Editor
/** @file SciTEWin.cxx
** Main code for the Windows version of the editor.
**/
// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#include <time.h>

#include "SciTEWin.h"

#ifndef NO_EXTENSIONS
#include "MultiplexExtension.h"

#ifndef NO_FILER
#include "DirectorExtension.h"
#endif

#ifndef NO_LUA
#include "SingleThreadExtension.h"
#include "LuaExtension.h"
#endif

#endif

const WCHAR appName[] = L"SciTE64";

long SciTEKeys::ParseKeyCode(const WCHAR *mnemonic) {
    int modsInKey = 0;
    int keyval = -1;

    if (mnemonic && *mnemonic) {
        SString sKey = mnemonic;

        if (sKey.contains(L"Ctrl+")) {
            modsInKey |= SCMOD_CTRL;
            sKey.remove(L"Ctrl+");
        }
        if (sKey.contains(L"Shift+")) {
            modsInKey |= SCMOD_SHIFT;
            sKey.remove(L"Shift+");
        }
        if (sKey.contains(L"Alt+")) {
            modsInKey |= SCMOD_ALT;
            sKey.remove(L"Alt+");
        }

        if (sKey.length() == 1) {
            keyval = VkKeyScan(sKey[0]) & 0xFF;
        } else if (sKey.length() > 1) {
            if ((sKey[0] == L'F') && (isdigit(sKey[1]))) {
                sKey.remove(L"F");
                int fkeyNum = sKey.value();
                if (fkeyNum >= 1 && fkeyNum <= 12)
                    keyval = fkeyNum - 1 + VK_F1;
            } else if ((sKey[0] == L'V') && (isdigit(sKey[1]))) {
                sKey.remove(L"V");
                int vkey = sKey.value();
                if (vkey > 0 && vkey <= 0x7FFF)
                    keyval = vkey;
            } else if (sKey.search(L"Keypad") == 0) {
                sKey.remove(L"Keypad");
                if (isdigit(sKey[0])) {
                    int keyNum = sKey.value();
                    if (keyNum >= 0 && keyNum <= 9)
                        keyval = keyNum + VK_NUMPAD0;
                } else if (sKey == L"Plus") {
                    keyval = VK_ADD;
                } else if (sKey == L"Minus") {
                    keyval = VK_SUBTRACT;
                } else if (sKey == L"Decimal") {
                    keyval = VK_DECIMAL;
                } else if (sKey == L"Divide") {
                    keyval = VK_DIVIDE;
                } else if (sKey == L"Multiply") {
                    keyval = VK_MULTIPLY;
                }
            } else if (sKey == L"Left") {
                keyval = VK_LEFT;
            } else if (sKey == L"Right") {
                keyval = VK_RIGHT;
            } else if (sKey == L"Up") {
                keyval = VK_UP;
            } else if (sKey == L"Down") {
                keyval = VK_DOWN;
            } else if (sKey == L"Insert") {
                keyval = VK_INSERT;
            } else if (sKey == L"End") {
                keyval = VK_END;
            } else if (sKey == L"Home") {
                keyval = VK_HOME;
            } else if (sKey == L"Enter") {
                keyval = VK_RETURN;
            } else if (sKey == L"Space") {
                keyval = VK_SPACE;
            } else if (sKey == L"Tab") {
                keyval = VK_TAB;
            } else if (sKey == L"Escape") {
                keyval = VK_ESCAPE;
            } else if (sKey == L"Delete") {
                keyval = VK_DELETE;
            } else if (sKey == L"PageUp") {
                keyval = VK_PRIOR;
            } else if (sKey == L"PageDown") {
                keyval = VK_NEXT;
            } else if (sKey == L"Win") {
                keyval = VK_LWIN;
            } else if (sKey == L"Menu") {
                keyval = VK_APPS;
            }
        }
    }

    return (keyval > 0) ? (keyval | (modsInKey<<16)) : 0;
}

bool SciTEKeys::MatchKeyCode(long parsedKeyCode, int keyval, int modifiers) {
    // TODO: are the 0x11 and 0x10 special cases needed, or are they
    // just short-circuits?  If not needed, this test could removed,
    // and perhaps the function moved to Platform.h as an inline.
    if (keyval == 0x11 || keyval == 0x10)
        return false;
    return parsedKeyCode && !(0xFFFF0000 & (keyval | modifiers)) && (parsedKeyCode == (keyval | (modifiers<<16)));
}

HINSTANCE SciTEWin::hInstance = 0;
WCHAR *SciTEWin::className = NULL;
WCHAR *SciTEWin::classNameInternal = NULL;
SciTEWin *SciTEWin::app = NULL;

SciTEWin::SciTEWin(Extension *ext) : SciTEBase(ext) {
    app = this;
    cmdShow = 0;
    heightBar = 7;
    fontTabs = 0;
    wFocus = 0;

    winPlace.length = 0;

    openWhat[0] = L'\0';
    memset(&fr, 0, sizeof(fr));
    modalParameters = false;
    filterDefault = 1;
    menuSource = 0;

    hWriteSubProcess = NULL;
    subProcessGroupId = 0;
    outputScroll = 1;

    // Read properties resource into propsEmbed
    // The embedded properties file is to allow distributions to be sure
    // that they have a sensible default configuration even if the properties
    // files are missing. Also allows a one file distribution of Sc1.EXE.
    propsEmbed.Clear();
    // System type properties are also stored in the embedded properties.
    propsEmbed.Set(L"PLAT_WIN", L"1");

    allowAlpha = true;
    propsEmbed.Set(L"PLAT_WINNT", L"1");

    HRSRC handProps = ::FindResource(hInstance, L"Embedded", L"Properties");
    if (handProps) {
        DWORD size = ::SizeofResource(hInstance, handProps);
        HGLOBAL hmem = ::LoadResource(hInstance, handProps);
        if (hmem) {
            const void *pv = ::LockResource(hmem);
            if (pv) {
                propsEmbed.ReadFromMemory(
                    reinterpret_cast<const WCHAR *>(pv), size, FilePath());
            }
        }
        ::FreeResource(handProps);
    }

    pathAbbreviations = GetAbbrevPropertiesFileName();

    ReadGlobalPropFile();
    /// Need to copy properties to variables before setting up window
    SetPropertiesInitial();
    ReadAbbrevPropFile();

    hDevMode = 0;
    hDevNames = 0;
    ::ZeroMemory(&pagesetupMargin, sizeof(pagesetupMargin));

    hHH = 0;
    hMM = 0;
    uniqueInstance.Init(this);

    hAccTable = ::LoadAccelerators(hInstance, L"ACCELS"); // md
}

SciTEWin::~SciTEWin() {
    if (hDevMode)
        ::GlobalFree(hDevMode);
    if (hDevNames)
        ::GlobalFree(hDevNames);
    if (hHH)
        ::FreeLibrary(hHH);
    if (hMM)
        ::FreeLibrary(hMM);
    if (fontTabs)
        ::DeleteObject(fontTabs);
}

uptr_t SciTEWin::GetInstance() {
    return reinterpret_cast<uptr_t>(hInstance);
}

void SciTEWin::Register(HINSTANCE hInstance_) {
    const WCHAR resourceName[] = L"SciTE";

    hInstance = hInstance_;

    WNDCLASS wndclass;

    // Register the frame window
    className = L"SciTEWindow";
    wndclass.style = 0;
    wndclass.lpfnWndProc = SciTEWin::TWndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = sizeof(SciTEWin*);
    wndclass.hInstance = hInstance;
    wndclass.hIcon = ::LoadIcon(hInstance, resourceName);
    wndclass.hCursor = NULL;
    wndclass.hbrBackground = NULL;
    wndclass.lpszMenuName = resourceName;
    wndclass.lpszClassName = className;
    if (!::RegisterClass(&wndclass))
        exit(FALSE);

    // Register the window that holds the two Scintilla edit windows and the separator
    classNameInternal = L"SciTEWindowContent";
    wndclass.lpfnWndProc = SciTEWin::IWndProc;
    wndclass.lpszMenuName = 0;
    wndclass.lpszClassName = classNameInternal;
    if (!::RegisterClass(&wndclass))
        exit(FALSE);
}

void SciTEWin::GetWindowPosition(int *left, int *top, int *width, int *height, int *maximize) {
    winPlace.length = sizeof(winPlace);
    ::GetWindowPlacement(MainHWND(), &winPlace);

    *left = winPlace.rcNormalPosition.left;
    *top = winPlace.rcNormalPosition.top;
    *width =  winPlace.rcNormalPosition.right - winPlace.rcNormalPosition.left;
    *height = winPlace.rcNormalPosition.bottom - winPlace.rcNormalPosition.top;
    *maximize = (winPlace.showCmd == SW_MAXIMIZE) ? 1 : 0;
}

void SciTEWin::ReadProperties() {
    SciTEBase::ReadProperties();

    outputScroll = props.GetInt(L"output.scroll", 1);
}

static FilePath GetSciTEPath(FilePath home) {
    if (home.IsSet()) {
        return FilePath(home);
    } else {
        WCHAR path[MAX_PATH];
        ::GetModuleFileName(0, path, sizeof(path) / sizeof(path[0]));
        // Remove the SciTE.exe
        WCHAR *lastSlash = wcsrchr(path, pathSepChar);
        if (lastSlash)
            *lastSlash = L'\0';
        return FilePath(path);
    }
}

FilePath SciTEWin::GetDefaultDirectory() {
    WCHAR *home = _wgetenv(L"SciTE_HOME");
    return GetSciTEPath(home);
}

FilePath SciTEWin::GetSciteDefaultHome() {
    WCHAR *home = _wgetenv(L"SciTE_HOME");
    return GetSciTEPath(home);
}

FilePath SciTEWin::GetSciteUserHome() {
    WCHAR *home = _wgetenv(L"SciTE_HOME");
    if (!home)
        home = _wgetenv(L"USERPROFILE");
    return GetSciTEPath(home);
}

// Help command lines contain topic!path
void SciTEWin::ExecuteOtherHelp(const WCHAR *cmd) {
    WCHAR *topic = StringDup(cmd);
    if (topic) {
        WCHAR *path = wcschr(topic, L'!');
        if (path) {
            *path = L'\0';
            path++;	// After the !
            ::WinHelp(MainHWND(),
                path,
                HELP_KEY,
                reinterpret_cast<unsigned long>(topic));
        }
    }
    delete []topic;
}

// HH_AKLINK not in mingw headers
struct XHH_AKLINK {
    long cbStruct;
    BOOL fReserved;
    const WCHAR *pszKeywords;
    WCHAR *pszUrl;
    WCHAR *pszMsgText;
    WCHAR *pszMsgTitle;
    WCHAR *pszWindow;
    BOOL fIndexOnFail;
};

// Help command lines contain topic!path
void SciTEWin::ExecuteHelp(const WCHAR *cmd) {
    if (!hHH)
        hHH = ::LoadLibrary(L"HHCTRL.OCX");

    if (hHH) {
        WCHAR *topic = StringDup(cmd);
        WCHAR *path = wcschr(topic, L'!');
        if (topic && path) {
            *path = L'\0';
            path++;	// After the !
            typedef HWND (WINAPI *HelpFn) (HWND, const WCHAR *, UINT, DWORD);
            HelpFn fnHHA = (HelpFn)::GetProcAddress(hHH, "HtmlHelpW");
            if (fnHHA) {
                XHH_AKLINK ak;
                ak.cbStruct = sizeof(ak);
                ak.fReserved = FALSE;
                ak.pszKeywords = topic;
                ak.pszUrl = NULL;
                ak.pszMsgText = NULL;
                ak.pszMsgTitle = NULL;
                ak.pszWindow = NULL;
                ak.fIndexOnFail = TRUE;
                fnHHA(NULL,
                    path,
                    0x000d,          	// HH_KEYWORD_LOOKUP
                    reinterpret_cast<DWORD>(&ak)
                    );
            }
        }
        delete []topic;
    }
}

void SciTEWin::CopyAsRTF() {
    Sci_CharacterRange cr = GetSelection();
    WCHAR *fileNameTemp = _wtmpnam(0);
    if (fileNameTemp) {
        SaveToRTF(fileNameTemp, cr.cpMin, cr.cpMax);
        FILE *fp = _wfopen(fileNameTemp, fileRead);
        if (fp) {
            fseek(fp, 0, SEEK_END);
            int len = ftell(fp);
            fseek(fp, 0, SEEK_SET);
            HGLOBAL hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, len + 1);
            if (hand) {
                ::OpenClipboard(MainHWND());
                ::EmptyClipboard();
                WCHAR *ptr = static_cast<WCHAR *>(::GlobalLock(hand));
                fread(ptr, 1, len, fp);
                ptr[len] = L'\0';
                ::GlobalUnlock(hand);
                ::SetClipboardData(::RegisterClipboardFormat(CF_RTF), hand);
                ::CloseClipboard();
            }
            fclose(fp);
        }
        _wunlink(fileNameTemp);
    }
}

void SciTEWin::FullScreenToggle() {
    HWND wTaskBar = FindWindow(L"Shell_TrayWnd", L"");
    fullScreen = !fullScreen;
    if (fullScreen) {
        ::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
        ::SystemParametersInfo(SPI_SETWORKAREA, 0, 0, SPIF_SENDCHANGE);
        ::ShowWindow(wTaskBar, SW_HIDE);

        winPlace.length = sizeof(winPlace);
        ::GetWindowPlacement(MainHWND(), &winPlace);
        int topStuff = ::GetSystemMetrics(SM_CYCAPTION) +
            ::GetSystemMetrics(SM_CYEDGE);
        if (props.GetInt(L"full.screen.hides.menu"))
            topStuff += ::GetSystemMetrics(SM_CYMENU);
        ::SetWindowPos(MainHWND(), HWND_TOP,
            -::GetSystemMetrics(SM_CXSIZEFRAME) - 1,
            -topStuff - 2,
            ::GetSystemMetrics(SM_CXSCREEN) +
            2 * ::GetSystemMetrics(SM_CXSIZEFRAME) + 2,
            ::GetSystemMetrics(SM_CYSCREEN) + topStuff +
            ::GetSystemMetrics(SM_CYSIZEFRAME) + 3,
            0);
    } else {
        ::ShowWindow(wTaskBar, SW_SHOW);
        if (winPlace.length) {
            ::SystemParametersInfo(SPI_SETWORKAREA, 0, &rcWorkArea, 0);
            if (winPlace.showCmd == SW_SHOWMAXIMIZED) {
                ::ShowWindow(MainHWND(), SW_RESTORE);
                ::ShowWindow(MainHWND(), SW_SHOWMAXIMIZED);
            } else {
                ::SetWindowPlacement(MainHWND(), &winPlace);
            }
        }
    }
    ::SetForegroundWindow(MainHWND());
    CheckMenus();
}

HWND SciTEWin::MainHWND() {
    return reinterpret_cast<HWND>(wSciTE.GetID());
}

void SciTEWin::Command(WPARAM wParam, LPARAM lParam) {
    int cmdID = ControlIDOfCommand(wParam);
    if (wParam & 0x10000) {
        // From accelerator -> goes to focused pane.
        menuSource = 0;
    }
    if (reinterpret_cast<HWND>(lParam) == wToolBar.GetID()) {
        // From toolbar -> goes to focused pane.
        menuSource = 0;
    }

    switch (cmdID) {

    case IDM_SRCWIN:
    case IDM_RUNWIN:
        if (HIWORD(wParam) == SCEN_SETFOCUS) {
            wFocus = reinterpret_cast<HWND>(lParam);
            CheckMenus();
        }
        if (HIWORD(wParam) == SCEN_KILLFOCUS) {
            CheckMenus();
        }
        break;

    case IDM_ACTIVATE:
        Activate(lParam);
        break;

    case IDM_FINISHEDEXECUTE: {
        jobQueue.SetExecuting(false);
        if (needReadProperties)
            ReadProperties();
        CheckMenus();
        for (int icmd = 0; icmd < jobQueue.commandMax; icmd++) {
            jobQueue.jobQueue[icmd].Clear();
        }
        jobQueue.commandCurrent = 0;
        CheckReload();
                              }
                              break;

    case IDM_ONTOP:
        topMost = (topMost ? false : true);
        ::SetWindowPos(MainHWND(), (topMost ? HWND_TOPMOST : HWND_NOTOPMOST ), 0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE);
        CheckAMenuItem(IDM_ONTOP, topMost);
        break;

    case IDM_OPENFILESHERE:
        uniqueInstance.ToggleOpenFilesHere();
        break;

    case IDM_FULLSCREEN:
        FullScreenToggle();
        break;

    case IDC_TABCLOSE:
        CloseTab((int)lParam);
        break;

    case IDC_SHIFTTAB:
        ShiftTab(LOWORD(lParam), HIWORD(lParam));
        break;

    default:
        SciTEBase::MenuCommand(cmdID, menuSource);
    }
}

/**
* Run a command with redirected input and output streams
* so the output can be put in a window.
* It is based upon several usenet posts and a knowledge base article.
* This is running in a separate thread to the user interface so should always
* use Platform::SendScintilla rather than a one of the direct function calls.
*/
DWORD SciTEWin::ExecuteOne(const Job &jobToRun, bool &seenOutput) {
    DWORD exitcode = 0;
    ElapsedTime commandTime;

    if (jobToRun.jobType == jobShell) {
        ShellExec(jobToRun.command, jobToRun.directory.AsFileSystem());
        return exitcode;
    }

    if (jobToRun.jobType == jobExtension) {
        if (extender) {
            // Problem: we are in the wrong thread!  That is the cause of the cursed PC.
            // It could also lead to other problems.

            if (jobToRun.flags & jobGroupUndo)
                Platform::SendScintilla(wEditor.GetID(), SCI_BEGINUNDOACTION);

            extender->OnExecute(jobToRun.command.c_str());

            if (jobToRun.flags & jobGroupUndo)
                Platform::SendScintilla(wEditor.GetID(), SCI_ENDUNDOACTION);

            Redraw();
            // A Redraw "might" be needed, since Lua and Director
            // provide enough low-level capabilities to corrupt the
            // display.
            // (That might have been due to a race condition, and might now be
            // corrected by SingleThreadExtension.  Should check it some time.)
        }
        return exitcode;
    }

    if (jobToRun.jobType == jobHelp) {
        ExecuteHelp(jobToRun.command.c_str());
        return exitcode;
    }

    if (jobToRun.jobType == jobOtherHelp) {
        ExecuteOtherHelp(jobToRun.command.c_str());
        return exitcode;
    }

    if (jobToRun.jobType == jobGrep) {
        // jobToRun.command is "(w|~)(c|~)(d|~)(b|~)\0files\0text"
        const WCHAR *grepCmd = jobToRun.command.c_str();
        GrepFlags gf = grepNone;
        if (*grepCmd == L'w')
            gf = static_cast<GrepFlags>(gf | grepWholeWord);
        grepCmd++;
        if (*grepCmd == L'c')
            gf = static_cast<GrepFlags>(gf | grepMatchCase);
        grepCmd++;
        if (*grepCmd == L'd')
            gf = static_cast<GrepFlags>(gf | grepDot);
        grepCmd++;
        if (*grepCmd == L'b')
            gf = static_cast<GrepFlags>(gf | grepBinary);
        const WCHAR *findFiles = grepCmd + 2;
        const WCHAR *findWhat = findFiles + wcslen(findFiles) + 1;
        InternalGrep(gf, jobToRun.directory.AsFileSystem(), findFiles, findWhat);
        return exitcode;
    }

    //OSVERSIONINFO osv = {sizeof(OSVERSIONINFO), 0, 0, 0, 0, ""};
    //::GetVersionEx(&osv);
    //bool windows95 = osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS;

    SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), 0, 0};
    WCHAR buffer[16384];
    //Platform::DebugPrintf("Execute <%s>\n", command);
    OutputAppendStringSynchronised(L">");
    OutputAppendStringSynchronised(jobToRun.command.c_str());
    OutputAppendStringSynchronised(L"\n");

    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = NULL;

    SECURITY_DESCRIPTOR sd;

    HANDLE hPipeWrite = NULL;
    HANDLE hPipeRead = NULL;
    // Create pipe for output redirection
    // read handle, write handle, security attributes,  number of bytes reserved for pipe - 0 default
    ::CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0);

    //Platform::DebugPrintf("2Execute <%s>\n");
    // Create pipe for input redirection. In this code, you do not
    // redirect the output of the child process, but you need a handle
    // to set the hStdInput field in the STARTUP_INFO struct. For safety,
    // you should not set the handles to an invalid handle.

    hWriteSubProcess = NULL;
    subProcessGroupId = 0;
    HANDLE hRead2 = NULL;
    // read handle, write handle, security attributes,  number of bytes reserved for pipe - 0 default
    ::CreatePipe(&hRead2, &hWriteSubProcess, &sa, 0);

    ::SetHandleInformation(hPipeRead, HANDLE_FLAG_INHERIT, 0);
    ::SetHandleInformation(hWriteSubProcess, HANDLE_FLAG_INHERIT, 0);

    //Platform::DebugPrintf("3Execute <%s>\n");
    // Make child process use hPipeWrite as standard out, and make
    // sure it does not show on screen.
    STARTUPINFO si = {
        sizeof(STARTUPINFO), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    if (jobToRun.jobType == jobCLI)
        si.wShowWindow = SW_HIDE;
    else
        si.wShowWindow = SW_SHOW;
    si.hStdInput = hRead2;
    si.hStdOutput = hPipeWrite;
    si.hStdError = hPipeWrite;

    FilePath startDirectory = jobToRun.directory.AbsolutePath();

    PROCESS_INFORMATION pi = {0, 0, 0, 0};

    bool running = ::CreateProcess(
        NULL,
        const_cast<WCHAR *>(jobToRun.command.c_str()),
        NULL, NULL,
        TRUE, CREATE_NEW_PROCESS_GROUP,
        NULL,
        startDirectory.IsSet() ?
        static_cast<const WCHAR *>(startDirectory.AsFileSystem()) : NULL,
        &si, &pi);

    if (running) {
        subProcessGroupId = pi.dwProcessId;

        bool cancelled = false;

        SString repSelBuf;

        DWORD timeDetectedDeath = 0;

        size_t totalBytesToWrite = 0;
        if (jobToRun.flags & jobHasInput) {
            totalBytesToWrite = jobToRun.input.length();
        }

        if (totalBytesToWrite > 0 && !(jobToRun.flags & jobQuiet)) {
            SString input = jobToRun.input;
            input.substitute(L"\n", L"\n>> ");

            OutputAppendStringSynchronised(L">> ");
            OutputAppendStringSynchronised(input.c_str());
            OutputAppendStringSynchronised(L"\n");
        }

        unsigned writingPosition = 0;

        while (running) {
            if (writingPosition >= totalBytesToWrite) {
                ::Sleep(100L);
            }

            DWORD bytesRead = 0;
            DWORD bytesAvail = 0;

            if (!::PeekNamedPipe(hPipeRead, buffer, sizeof(buffer) / sizeof(buffer[0]), &bytesRead, &bytesAvail, NULL)) {
                    bytesAvail = 0;
            }

            if ((bytesAvail < 1000) && (hWriteSubProcess != INVALID_HANDLE_VALUE) && (writingPosition < totalBytesToWrite)) {
                // There is input to transmit to the process.  Do it in small blocks, interleaved
                // with reads, so that our hRead buffer will not be overrun with results.

                size_t bytesToWrite;
                int eol_pos = jobToRun.input.search(L"\n", writingPosition);
                if (eol_pos == -1) {
                    bytesToWrite = totalBytesToWrite - writingPosition;
                } else {
                    bytesToWrite = eol_pos + 1 - writingPosition;
                }
                if (bytesToWrite > 250) {
                    bytesToWrite = 250;
                }

                DWORD bytesWrote = 0;

                int bTest = ::WriteFile(hWriteSubProcess, const_cast<WCHAR *>(jobToRun.input.c_str() + writingPosition), bytesToWrite, &bytesWrote, NULL);

                if (bTest) {
                    if ((writingPosition + bytesToWrite) / 1024 > writingPosition / 1024) {
                        // sleep occasionally, even when writing
                        ::Sleep(100L);
                    }

                    writingPosition += bytesWrote;

                    if (writingPosition >= totalBytesToWrite) {
                        ::CloseHandle(hWriteSubProcess);
                        hWriteSubProcess = INVALID_HANDLE_VALUE;
                    }

                } else {
                    // Is this the right thing to do when writing to the pipe fails?
                    ::CloseHandle(hWriteSubProcess);
                    hWriteSubProcess = INVALID_HANDLE_VALUE;
                    OutputAppendStringSynchronised(L"\n>Input pipe closed due to write failure.\n");
                }

            } else if (bytesAvail > 0) {
                int bTest = ::ReadFile(hPipeRead, buffer, sizeof(buffer), &bytesRead, NULL);

                if (bTest && bytesRead) {

                    if (jobToRun.flags & jobRepSelMask) {
                        repSelBuf.append(buffer, bytesRead);
                    }

                    if (!(jobToRun.flags & jobQuiet)) {
                        if (!seenOutput) {
                            MakeOutputVisible();
                            seenOutput = true;
                        }
                        // Display the data
                        OutputAppendStringSynchronised(buffer, bytesRead);
                    }

                    ::UpdateWindow(MainHWND());
                } else {
                    running = false;
                }
            } else {
                if (::GetExitCodeProcess(pi.hProcess, &exitcode)) {
                    if (STILL_ACTIVE != exitcode) {
                        running = false;
                    }
                }
            }

            if (jobQueue.SetCancelFlag(0)) {
                if (WAIT_OBJECT_0 != ::WaitForSingleObject(pi.hProcess, 500)) {
                    // We should use it only if the GUI process is stuck and
                    // don't answer to a normal termination command.
                    // This function is dangerous: dependant DLLs don't know the process
                    // is terminated, and memory isn't released.
                    OutputAppendStringSynchronised(L"\n>Process failed to respond; forcing abrupt termination...\n");
                    ::TerminateProcess(pi.hProcess, 1);
                }
                running = false;
                cancelled = true;
            }
        }

        if (WAIT_OBJECT_0 != ::WaitForSingleObject(pi.hProcess, 1000)) {
            OutputAppendStringSynchronised(L"\n>Process failed to respond; forcing abrupt termination...");
            ::TerminateProcess(pi.hProcess, 2);
        }
        ::GetExitCodeProcess(pi.hProcess, &exitcode);
        SString sExitMessage(exitcode);
        sExitMessage.insert(0, L">Exit code: ");
        if (jobQueue.TimeCommands()) {
            sExitMessage += L"    Time: ";
            sExitMessage += SString(commandTime.Duration(), 3);
        }
        sExitMessage += L"\n";
        OutputAppendStringSynchronised(sExitMessage.c_str());

        ::CloseHandle(pi.hProcess);
        ::CloseHandle(pi.hThread);

        if (!cancelled) {
            bool doRepSel = false;
            if (jobToRun.flags & jobRepSelYes)
                doRepSel = true;
            else if (jobToRun.flags & jobRepSelAuto)
                doRepSel = (0 == exitcode);

            if (doRepSel) {
                int cpMin = Platform::SendScintilla(wEditor.GetID(), SCI_GETSELECTIONSTART, 0, 0);
                Platform::SendScintilla(wEditor.GetID(),SCI_REPLACESEL,0,(sptr_t)(repSelBuf.c_str()));
                Platform::SendScintilla(wEditor.GetID(), SCI_SETSEL, cpMin, cpMin+repSelBuf.length());
            }
        }

        WarnUser(warnExecuteOK);

    } else {
        DWORD nRet = ::GetLastError();
        LPTSTR lpMsgBuf = NULL;
        ::FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            nRet,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),   // Default language
            reinterpret_cast<LPTSTR>(&lpMsgBuf),
            0,
            NULL
            );
        OutputAppendStringSynchronised(L">");
        OutputAppendStringSynchronised(lpMsgBuf);
        ::LocalFree(lpMsgBuf);
        WarnUser(warnExecuteKO);
    }
    ::CloseHandle(hPipeRead);
    ::CloseHandle(hPipeWrite);
    ::CloseHandle(hRead2);
    ::CloseHandle(hWriteSubProcess);
    hWriteSubProcess = NULL;
    subProcessGroupId = 0;
    return exitcode;
}

/**
* Run the commands in the job queue, stopping if one fails.
* This is running in a separate thread to the user interface so must be
* careful when reading and writing shared state.
*/
void SciTEWin::ProcessExecute() {
    DWORD exitcode = 0;
    if (scrollOutput)
        SendOutputEx(SCI_GOTOPOS, SendOutputEx(SCI_GETTEXTLENGTH, 0, 0, false),0, false);
    int originalEnd = SendOutputEx(SCI_GETCURRENTPOS, 0, 0, false);
    bool seenOutput = false;

    for (int icmd = 0; icmd < jobQueue.commandCurrent && icmd < jobQueue.commandMax && exitcode == 0; icmd++) {
        exitcode = ExecuteOne(jobQueue.jobQueue[icmd], seenOutput);
        if (jobQueue.isBuilding) {
            // The build command is first command in a sequence so it is only built if
            // that command succeeds not if a second returns after document is modified.
            jobQueue.isBuilding = false;
            if (exitcode == 0)
                jobQueue.isBuilt = true;
        }
    }

    // Move selection back to beginning of this run so that F4 will go
    // to first error of this run.
    // scroll and return only if output.scroll equals
    // one in the properties file
    if ((outputScroll == 1) && returnOutputToCommand)
        SendOutputEx(SCI_GOTOPOS, originalEnd, 0, false);
    returnOutputToCommand = true;
    ::SendMessage(MainHWND(), WM_COMMAND, IDM_FINISHEDEXECUTE, 0);
}

void ExecThread(void *ptw) {
    SciTEWin *tw = reinterpret_cast<SciTEWin *>(ptw);
    tw->ProcessExecute();
}

struct ShellErr {
    DWORD code;
    const WCHAR *descr;
};

void SciTEWin::ShellExec(const SString &cmd, const WCHAR *dir) {
    WCHAR *mycmd;

    // guess if cmd is an executable, if this succeeds it can
    // contain spaces without enclosing it with "
    WCHAR *mycmdcopy = StringDup(cmd.c_str());
    _wcslwr(mycmdcopy);

    WCHAR *mycmd_end = NULL;
    WCHAR *myparams = NULL;

    WCHAR *s = wcsstr(mycmdcopy, L".exe");
    if (s == NULL)
        s = wcsstr(mycmdcopy, L".cmd");
    if (s == NULL)
        s = wcsstr(mycmdcopy, L".bat");
    if (s == NULL)
        s = wcsstr(mycmdcopy, L".com");
    if ((s != NULL) && ((*(s + 4) == L'\0') || (*(s + 4) == ' '))) {
        int len_mycmd = s - mycmdcopy + 4;
        delete []mycmdcopy;
        mycmdcopy = StringDup(cmd.c_str());
        mycmd = mycmdcopy;
        mycmd_end = mycmdcopy + len_mycmd;
    } else {
        delete []mycmdcopy;
        mycmdcopy = StringDup(cmd.c_str());
        if (*mycmdcopy != L'"') {
            // get next space to separate cmd and parameters
            mycmd_end = wcschr(mycmdcopy, L' ');
            mycmd = mycmdcopy;
        } else {
            // the cmd is surrounded by ", so it can contain spaces, but we must
            // strip the " for ShellExec
            mycmd = mycmdcopy + 1;
            WCHAR *s = wcschr(mycmdcopy + 1, L'"');
            if (s != NULL) {
                *s = L'\0';
                mycmd_end = s + 1;
            }
        }
    }

    if ((mycmd_end != NULL) && (*mycmd_end != L'\0')) {
        *mycmd_end = L'\0';
        // test for remaining params after cmd, they may be surrounded by " but
        // we give them as-is to ShellExec
        ++mycmd_end;
        while (*mycmd_end == L' ')
            ++mycmd_end;

        if (*mycmd_end != L'\0')
            myparams = mycmd_end;
    }

    uptr_t rc = reinterpret_cast<uptr_t>(
        ::ShellExecute(
        MainHWND(),          // parent wnd for msgboxes during app start
        NULL,           // cmd is open
        mycmd,          // file to open
        myparams,          // parameters
        dir,          // launch directory
        SW_SHOWNORMAL)); //default show cmd

    if (rc > 32) {
        // it worked!
        delete []mycmdcopy;
        return;
    }

    const int numErrcodes = 15;
    static const ShellErr field[numErrcodes] = {
        { 0, L"The operating system is out of memory or resources." },
        { ERROR_FILE_NOT_FOUND, L"The specified file was not found." },
        { ERROR_PATH_NOT_FOUND, L"The specified path was not found." },
        { ERROR_BAD_FORMAT, L"The .exe file is invalid (non-Win32\256 .exe or error in .exe image)." },
        { SE_ERR_ACCESSDENIED, L"The operating system denied access to the specified file." },
        { SE_ERR_ASSOCINCOMPLETE, L"The file name association is incomplete or invalid." },
        { SE_ERR_DDEBUSY, L"The DDE transaction could not be completed because other DDE transactions were being processed." },
        { SE_ERR_DDEFAIL, L"The DDE transaction failed." },
        { SE_ERR_DDETIMEOUT, L"The DDE transaction could not be completed because the request timed out." },
        { SE_ERR_DLLNOTFOUND, L"The specified dynamic-link library was not found." },
        { SE_ERR_FNF, L"The specified file was not found." },
        { SE_ERR_NOASSOC, L"There is no application associated with the given file name extension." },
        { SE_ERR_OOM, L"There was not enough memory to complete the operation." },
        { SE_ERR_PNF, L"The specified path was not found." },
        { SE_ERR_SHARE, L"A sharing violation occurred." },
    };

    int i;
    for (i = 0; i < numErrcodes; ++i) {
        if (field[i].code == rc)
            break;
    }

    SString errormsg(L"Error while launching:\n\"");
    errormsg += mycmdcopy;
    if (myparams != NULL) {
        errormsg += L"\" with Params:\n\"";
        errormsg += myparams;
    }
    errormsg += L"\"\n";
    if (i < numErrcodes) {
        errormsg += field[i].descr;
    } else {
        errormsg += L"Unknown error code: ";
        errormsg += SString(rc);
    }
    WindowMessageBox(wSciTE, errormsg, MB_OK);

    delete []mycmdcopy;
}

void SciTEWin::Execute() {
    SciTEBase::Execute();

    _beginthread(ExecThread, 1024 * 1024, reinterpret_cast<void *>(this));
}

void SciTEWin::StopExecute() {
    if (hWriteSubProcess && (hWriteSubProcess != INVALID_HANDLE_VALUE)) {
        WCHAR stop[] = L"\032";
        DWORD bytesWrote = 0;
        ::WriteFile(hWriteSubProcess, stop, static_cast<DWORD>(wcslen(stop)), &bytesWrote, NULL);
        Sleep(500L);
    }

#ifdef USE_CONSOLE_EVENT
    if (subProcessGroupId) {
        // this also doesn't work
        OutputAppendStringSynchronised("\n>Attempting to cancel process...");

        if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, subProcessGroupId)) {
            LONG errCode = GetLastError();
            OutputAppendStringSynchronised("\n>BREAK Failed ");
            OutputAppendStringSynchronised(SString(errCode).c_str());
            OutputAppendStringSynchronised("\n");
        }
        Sleep(100L);
    }
#endif

    jobQueue.SetCancelFlag(1);
}

void SciTEWin::AddCommand(const SString &cmd, const SString &dir, JobSubsystem jobType, const SString &input, int flags) {
    if (cmd.length()) {
        if ((jobType == jobShell) && ((flags & jobForceQueue) == 0)) {
            SString pCmd = cmd;
            parameterisedCommand = L"";
            if (pCmd[0] == L'*') {
                pCmd.remove(0, 1);
                parameterisedCommand = pCmd;
                if (!ParametersDialog(true)) {
                    return;
                }
            } else {
                ParamGrab();
            }
            pCmd = props.Expand(pCmd.c_str());
            ShellExec(pCmd, dir.c_str());
        } else {
            SciTEBase::AddCommand(cmd, dir, jobType, input, flags);
        }
    }
}

void SciTEWin::QuitProgram() {
    if (SaveIfUnsureAll() != IDCANCEL) {
        if (fullScreen)	// Ensure tray visible on exit
            FullScreenToggle();
        ::PostQuitMessage(0);
        wSciTE.Destroy();
    }
}

void SciTEWin::RestorePosition() {
    int left = propsSession.GetInt(L"position.left", CW_USEDEFAULT);
    int top = propsSession.GetInt(L"position.top", CW_USEDEFAULT);
    int width = propsSession.GetInt(L"position.width", CW_USEDEFAULT);
    int height = propsSession.GetInt(L"position.height", CW_USEDEFAULT);
    cmdShow = propsSession.GetInt(L"position.maximize", 0) ? SW_MAXIMIZE : 0;

    if (left != static_cast<int>(CW_USEDEFAULT) &&
        top != static_cast<int>(CW_USEDEFAULT) &&
        width != static_cast<int>(CW_USEDEFAULT) &&
        height != static_cast<int>(CW_USEDEFAULT)) {
            winPlace.length = sizeof(winPlace);
            winPlace.rcNormalPosition.left = left;
            winPlace.rcNormalPosition.right = left + width;
            winPlace.rcNormalPosition.top = top;
            winPlace.rcNormalPosition.bottom = top + height;
            ::SetWindowPlacement(MainHWND(), &winPlace);
    }
}

void SciTEWin::CreateUI() {
    CreateBuffers();

    int left = props.GetInt(L"position.left", CW_USEDEFAULT);
    int top = props.GetInt(L"position.top", CW_USEDEFAULT);
    int width = props.GetInt(L"position.width", CW_USEDEFAULT);
    int height = props.GetInt(L"position.height", CW_USEDEFAULT);
    cmdShow = props.GetInt(L"position.maximize", 0) ? SW_MAXIMIZE : 0;
    if (width == -1 || height == -1) {
        cmdShow = SW_MAXIMIZE;
        width = CW_USEDEFAULT;
        height = CW_USEDEFAULT;
    }

    if (props.GetInt(L"position.tile") && ::FindWindow(L"SciTEWindow", NULL) &&
        (left != static_cast<int>(CW_USEDEFAULT))) {
            left += width;
    }
    // Pass 'this' pointer in lpParam of CreateWindow().
    wSciTE = ::CreateWindowEx(
        0,
        className,
        windowName.c_str(),
        WS_CAPTION | WS_SYSMENU | WS_THICKFRAME |
        WS_MINIMIZEBOX | WS_MAXIMIZEBOX |
        WS_CLIPCHILDREN,
        left, top, width, height,
        NULL,
        NULL,
        hInstance,
        reinterpret_cast<LPVOID>(this));
    if (!wSciTE.Created())
        exit(FALSE);

    if (props.GetInt(L"save.position"))
        RestorePosition();

    LocaliseMenus();
    LocaliseAccelerators();
    SString pageSetup = props.Get(L"print.margins");
    WCHAR val[32];
    WCHAR *ps = StringDup(pageSetup.c_str());
    const WCHAR *next = GetNextPropItem(ps, val, 32);
    pagesetupMargin.left = _wtol(val);
    next = GetNextPropItem(next, val, 32);
    pagesetupMargin.right = _wtol(val);
    next = GetNextPropItem(next, val, 32);
    pagesetupMargin.top = _wtol(val);
    GetNextPropItem(next, val, 32);
    pagesetupMargin.bottom = _wtol(val);
    delete []ps;

    UIAvailable();
}

static bool IsASpace(int ch) {
    return (ch == L' ') || (ch == L'\t');
}

/**
* Break up the command line into individual arguments and strip double quotes
* from each argument.
* @return A string with each argument separated by '\n'.
*/
SString SciTEWin::ProcessArgs(const WCHAR *cmdLine) {
    SString args;
    const WCHAR *startArg = cmdLine;
    while (*startArg) {
        while (IsASpace(*startArg)) {
            startArg++;
        }
        const WCHAR *endArg = startArg;
        if (*startArg == L'"') {	// Opening double-quote
            startArg++;
            endArg = startArg;
            while (*endArg && *endArg != L'\"') {
                endArg++;
            }
        } else {	// No double-quote, end of argument on first space
            while (*endArg && !IsASpace(*endArg)) {
                endArg++;
            }
        }
        SString arg(startArg, 0, endArg - startArg);
        args.appendwithseparator(arg.c_str(), L'\n');
        startArg = endArg;	// On a space or a double-quote, or on the end of the command line
        if (*startArg) {
            startArg++;
        }
    }

    return args;
}

/**
* Process the command line, check for other instance wanting to open files,
* create the SciTE window, perform batch processing (print) or transmit command line
* to other instance and exit or just show the window and open files.
*/
void SciTEWin::Run(const WCHAR *cmdLine) {
    // Load the default session file
    if (props.GetInt(L"save.session") || props.GetInt(L"save.position") || props.GetInt(L"save.recent")) {
        LoadSessionFile(L"");
    }

    // Break up the command line into individual arguments
    SString args = ProcessArgs(cmdLine);
    // Read the command line parameters:
    // In case the check.if.already.open property has been set or reset on the command line,
    // we still get a last chance to force checking or to open a separate instance;
    // Check if the user just want to print the file(s).
    // Don't process files yet.
    bool bBatchProcessing = ProcessCommandLine(args, 0);

    // No need to check for other instances when doing a batch job:
    // perform some tasks and exit immediately.
    if (!bBatchProcessing && props.GetInt(L"check.if.already.open") != 0) {
        uniqueInstance.CheckOtherInstance();
    }

    // We create the window, so it can be found by EnumWindows below,
    // and the Scintilla control is thus created, allowing to print the file(s).
    // We don't show it yet, so if it is destroyed (duplicate instance), it will
    // not flash on the taskbar or on the display.
    CreateUI();

    if (bBatchProcessing) {
        // Reprocess the command line and read the files
        ProcessCommandLine(args, 1);
        Print(false);	// Don't ask user for print parameters
        // Done, we exit the program
        ::PostQuitMessage(0);
        wSciTE.Destroy();
        return;
    }

    if (props.GetInt(L"check.if.already.open") != 0 && uniqueInstance.FindOtherInstance()) {
        uniqueInstance.SendCommands(cmdLine);

        // Kill itself, leaving room to the previous instance
        ::PostQuitMessage(0);
        wSciTE.Destroy();
        return;	// Don't do anything else
    }

    // OK, the instance will be displayed
    SizeSubWindows();
    wSciTE.Show();
    if (cmdShow) {	// assume SW_MAXIMIZE only
        ::ShowWindow(MainHWND(), cmdShow);
    }

    // Open all files given on command line.
    // The filenames containing spaces must be enquoted.
    // In case of not using buffers they get closed immediately except
    // the last one, but they move to the MRU file list
    ProcessCommandLine(args, 1);
    Redraw();
}

/**
* Draw the split bar.
*/
void SciTEWin::Paint(Surface *surfaceWindow, PRectangle) {
    PRectangle rcInternal = GetClientRectangle();
    //surfaceWindow->FillRectangle(rcInternal, Colour(0xff,0x80,0x80));

    int heightClient = rcInternal.Height();
    int widthClient = rcInternal.Width();

    int heightEditor = heightClient - heightOutput - heightBar;
    int yBorder = heightEditor;
    int xBorder = widthClient - heightOutput - heightBar;
    for (int i = 0; i < heightBar; i++) {
        if (i == 1)
            surfaceWindow->PenColour(GetSysColor(COLOR_3DHIGHLIGHT));
        else if (i == heightBar - 2)
            surfaceWindow->PenColour(GetSysColor(COLOR_3DSHADOW));
        else if (i == heightBar - 1)
            surfaceWindow->PenColour(GetSysColor(COLOR_3DDKSHADOW));
        else
            surfaceWindow->PenColour(GetSysColor(COLOR_3DFACE));
        if (splitVertical) {
            surfaceWindow->MoveTo(xBorder + i, 0);
            surfaceWindow->LineTo(xBorder + i, heightClient);
        } else {
            surfaceWindow->MoveTo(0, yBorder + i);
            surfaceWindow->LineTo(widthClient, yBorder + i);
        }
    }
}

void SciTEWin::AboutDialog() {
#ifdef STATIC_BUILD
    AboutDialogWithBuild(1);
#else
    AboutDialogWithBuild(0);
#endif
}

/**
* Open files dropped on the SciTE window.
*/
void SciTEWin::DropFiles(HDROP hdrop) {
    // If drag'n'drop inside the SciTE window but outside
    // Scintilla, hdrop is null, and an exception is generated!
    if (hdrop) {
        int filesDropped = ::DragQueryFile(hdrop, 0xffffffff, NULL, 0);
        for (int i = 0; i < filesDropped; ++i) {
            WCHAR pathDropped[MAX_PATH];
            ::DragQueryFile(hdrop, i, pathDropped, sizeof(pathDropped) / sizeof(pathDropped[0]));
            if (!Open(pathDropped)) {
                break;
            }
        }
        ::DragFinish(hdrop);
        // Put SciTE to forefront
        // May not work for Win2k, but OK for lower versions
        // Note: how to drop a file to an iconic window?
        // Actually, it is the Send To command that generates a drop.
        if (::IsIconic(MainHWND())) {
            ::ShowWindow(MainHWND(), SW_RESTORE);
        }
        ::SetForegroundWindow(MainHWND());
    }
}

/**
* Handle simple wild-card file patterns and directory requests.
*/
bool SciTEWin::PreOpenCheck(const WCHAR *arg) {
    bool isHandled = false;
    HANDLE hFFile;
    WIN32_FIND_DATA ffile;
    DWORD fileattributes = ::GetFileAttributes(arg);
    WCHAR filename[MAX_PATH];
    int nbuffers = props.GetInt(L"buffers");

    if (fileattributes != (DWORD) -1) {	// arg is an existing directory or filename
        // if the command line argument is a directory, use OpenDialog()
        if (fileattributes & FILE_ATTRIBUTE_DIRECTORY) {
            OpenDialog(FilePath(arg), props.GetExpanded(L"open.filter").c_str());
            isHandled = true;
        }
    } else if (nbuffers > 1 && (hFFile = ::FindFirstFile(arg, &ffile)) != INVALID_HANDLE_VALUE) {
        // If several buffers is accepted and the arg is a filename pattern matching at least an existing file
        isHandled = true;
        wcscpy(filename, arg);
        WCHAR *lastslash;
        if (NULL == (lastslash = wcsrchr(filename, L'\\')))
            lastslash = filename;	// No path
        else
            lastslash++;
        // Open files matching the given pattern until no more files or all available buffers are exhausted
        do {
            if (!(ffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {	// Skip directories
                wcscpy(lastslash, ffile.cFileName);
                Open(filename);
                --nbuffers;
            }
        } while (nbuffers > 0 && ::FindNextFile(hFFile, &ffile));
        ::FindClose(hFFile);
    } else {
        const WCHAR *lastslash = wcsrchr(arg, L'\\');
        const WCHAR *lastdot = wcsrchr(arg, L'.');

        // if the filename is only an extension, open the dialog box with it as the extension filter
        if (lastslash && lastdot && lastslash == lastdot - 1 || !lastslash && lastdot == arg) {
            isHandled = true;

            WCHAR dir[MAX_PATH];
            if (lastslash) { // the arg contains a path, so copy that part to dirName
                wcsncpy(dir, arg, lastslash - arg + 1);
                dir[lastslash - arg + 1] = L'\0';
            } else {
                wcscpy(dir, L".\\");
            }

            wcscpy(filename, L"*");
            wcscat(filename, lastdot);
            wcscat(filename, L"|");
            wcscat(filename, L"*");
            wcscat(filename, lastdot);
            OpenDialog(FilePath(dir), filename);
        } else if (!lastdot || lastslash && lastdot < lastslash) {
            // if the filename has no extension, try to match a file with list of standard extensions
            SString extensions = props.GetExpanded(L"source.default.extensions");
            if (extensions.length()) {
                wcscpy(filename, arg);
                WCHAR *endfilename = filename + wcslen(filename);
                extensions.substitute(L'|', L'\0');
                size_t start = 0;
                while (start < extensions.length()) {
                    const WCHAR *filterName = extensions.c_str() + start;
                    wcscpy(endfilename, filterName);
                    if (::GetFileAttributes(filename) != (DWORD) -1) {
                        isHandled = true;
                        Open(filename);
                        break;	// Found!
                    } else {
                        // Next extension
                        start += wcslen(extensions.c_str() + start) + 1;
                    }
                }
            }
        }
    }

    return isHandled;
}

/* return true if stdin is blocked:
- stdin is the console (isatty() == 1)
- a valid handle for stdin cannot be generated
- the handle appears to be the console - this may be a duplicate of using isatty() == 1
- the pipe cannot be peeked, which appears to be from command lines such as "scite <file.txt"
otherwise it is unblocked
*/
bool SciTEWin::IsStdinBlocked() {
    DWORD unread_messages;
    INPUT_RECORD irec[1];
    WCHAR bytebuffer;
    HANDLE hStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
    if (hStdIn == INVALID_HANDLE_VALUE) {
        /* an invalid handle, assume that stdin is blocked by falling to bottomn */;
    } else if (::PeekConsoleInput(hStdIn, irec, 1, &unread_messages) != 0) {
        /* it is the console, assume that stdin is blocked by falling to bottomn */;
    } else if (::GetLastError() == ERROR_INVALID_HANDLE) {
		for (int n = 0; n < 4; n++) {
            /*	if this fails, it is either
            - a busy pipe "scite \*.,cxx /s /b | s -@",
            - another type of pipe "scite - <file", or
            - a blocked pipe "findstrin nothing | scite -"
            in any case case, retry in a short bit
            */
            if (::PeekNamedPipe(hStdIn, &bytebuffer, sizeof(bytebuffer), NULL,NULL, &unread_messages) != 0) {
                if (unread_messages != 0) {
                    return false; /* is a pipe and it is not blocked */
                }
            }
            ::Sleep(2500);
        }
    }
    return true;
}

void SciTEWin::MinimizeToTray() {
    WCHAR n[64] = L"SciTE64";
    NOTIFYICONDATA nid;
    memset(&nid, 0, sizeof(nid));
    nid.cbSize = sizeof(nid);
    nid.hWnd = MainHWND();
    nid.uID = 1;
    nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
    nid.uCallbackMessage = SCITE_TRAY;
    nid.hIcon = static_cast<HICON>(
        ::LoadImage(hInstance, L"SCITE", IMAGE_ICON, 16, 16, LR_DEFAULTSIZE));
    wcscpy(nid.szTip, n);
    ::ShowWindow(MainHWND(), SW_MINIMIZE);
    if (::Shell_NotifyIcon(NIM_ADD, &nid)) {
        ::ShowWindow(MainHWND(), SW_HIDE);
    }
}

void SciTEWin::RestoreFromTray() {
    NOTIFYICONDATA nid;
    memset(&nid, 0, sizeof(nid));
    nid.cbSize = sizeof(nid);
    nid.hWnd = MainHWND();
    nid.uID = 1;
    ::ShowWindow(MainHWND(), SW_SHOW);
    ::Sleep(100);
    ::Shell_NotifyIcon(NIM_DELETE, &nid);
}

#ifndef VK_OEM_2
static const int VK_OEM_2=0xbf;
static const int VK_OEM_3=0xc0;
static const int VK_OEM_4=0xdb;
static const int VK_OEM_5=0xdc;
static const int VK_OEM_6=0xdd;
#endif
#ifndef VK_OEM_PLUS
static const int VK_OEM_PLUS=0xbb;
#endif

inline bool KeyMatch(const SString &sKey, int keyval, int modifiers) {
    return SciTEKeys::MatchKeyCode(
        SciTEKeys::ParseKeyCode(sKey.c_str()), keyval, modifiers);
}

LRESULT SciTEWin::KeyDown(WPARAM wParam) {
    // Look through lexer menu
    int modifiers =
        (Platform::IsKeyDown(VK_SHIFT) ? SCMOD_SHIFT : 0) |
        (Platform::IsKeyDown(VK_CONTROL) ? SCMOD_CTRL : 0) |
        (Platform::IsKeyDown(VK_MENU) ? SCMOD_ALT : 0);

    if (extender && extender->OnKey(wParam, modifiers))
        return 1l;

    for (int j = 0; j < languageItems; j++) {
        if (KeyMatch(languageMenu[j].menuKey, wParam, modifiers)) {
            SciTEBase::MenuCommand(IDM_LANGUAGE + j);
            return 1l;
        }
    }

    // loop through the Tools menu's active commands.
    HMENU hMenu = ::GetMenu(MainHWND());
    HMENU hToolsMenu = ::GetSubMenu(hMenu, menuTools);
    for (int tool_i = 0; tool_i < toolMax; ++tool_i) {
        MENUITEMINFO mii;
        mii.cbSize = sizeof(MENUITEMINFO);
        mii.fMask = MIIM_DATA;
        if (::GetMenuItemInfo(hToolsMenu, IDM_TOOLS+tool_i, FALSE, &mii) && mii.dwItemData) {
            if (SciTEKeys::MatchKeyCode(reinterpret_cast<long&>(mii.dwItemData), wParam, modifiers)) {
                SciTEBase::MenuCommand(IDM_TOOLS+tool_i);
                return 1l;
            }
        }
    }

    // loop through the keyboard short cuts defined by user.. if found
    // exec it the command defined
    for (int cut_i = 0; cut_i < shortCutItems; cut_i++) {
        if (KeyMatch(shortCutItemList[cut_i].menuKey, wParam, modifiers)) {
            int commandNum = SciTEBase::GetMenuCommandAsInt(shortCutItemList[cut_i].menuCommand);
            if (commandNum != -1) {
                // its possible that the command is for scintilla directly
                // all scintilla commands are larger then 2000
                if (commandNum < 2000) {
                    SciTEBase::MenuCommand(commandNum);
                } else {
                    SciTEBase::SendFocused(commandNum);
                }
                return 1l;
            }
        }
    }

    return 0l;
}

LRESULT SciTEWin::KeyUp(WPARAM wParam) {
    if (wParam == VK_CONTROL) {
        EndStackedTabbing();
    }
    return 0l;
}

void SciTEWin::AddToPopUp(const WCHAR *label, int cmd, bool enabled) {
    SString localised = localiser.Text(label);
    HMENU menu = reinterpret_cast<HMENU>(popup.GetID());
	if (0 == localised.length()){
        ::AppendMenu(menu, MF_SEPARATOR, 0, L"");
	}
	else if (enabled){
        ::AppendMenu(menu, MF_STRING, cmd, localised.c_str());
	}
	else{
        ::AppendMenu(menu, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, localised.c_str());
	}
}

LRESULT SciTEWin::ContextMenuMessage(UINT iMessage, WPARAM wParam, LPARAM lParam) {
    Window w = wEditor;
    Point pt = Point::FromLong(lParam);
    if ((pt.x == -1) && (pt.y == -1)) {
        // Caused by keyboard so display menu near caret
        if (wOutput.HasFocus())
            w = wOutput;
        int position = SendFocused(SCI_GETCURRENTPOS);
        pt.x = SendFocused(SCI_POINTXFROMPOSITION, 0, position);
        pt.y = SendFocused(SCI_POINTYFROMPOSITION, 0, position);
        POINT spt = {pt.x, pt.y};
        ::ClientToScreen(static_cast<HWND>(w.GetID()), &spt);
        pt = Point(spt.x, spt.y);
    } else {
        PRectangle rcEditor = wEditor.GetPosition();
        if (!rcEditor.Contains(pt)) {
            PRectangle rcOutput = wOutput.GetPosition();
            if (rcOutput.Contains(pt)) {
                w = wOutput;
            } else {	// In frame so use default.
                return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
            }
        }
    }
    menuSource = ::GetDlgCtrlID(reinterpret_cast<HWND>(w.GetID()));
    ContextMenu(w, pt, wSciTE);
    return 0;
}

LRESULT SciTEWin::WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) {
    int statusFailure = 0;
    static int boxesVisible = 0;
    try {
        //Platform::DebugPrintf("start wnd proc %x %x\n",iMessage, MainHWND());
        LRESULT uim = uniqueInstance.CheckMessage(iMessage, wParam, lParam);
        if (uim != 0) {
            return uim;
        }

        switch (iMessage) {

        case WM_CREATE:
            Creation();
            break;

        case WM_COMMAND:
            Command(wParam, lParam);
            break;

        case WM_CONTEXTMENU:
            return ContextMenuMessage(iMessage, wParam, lParam);

        case WM_ENTERMENULOOP:
            if (!wParam)
                menuSource = 0;
            break;

        case WM_SYSCOMMAND:
            if ((wParam == SC_MINIMIZE) && props.GetInt(L"minimize.to.tray")) {
                MinimizeToTray();
                return 0;
            }
            return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);

        case SCITE_TRAY:
            if (lParam == WM_LBUTTONDBLCLK) {
                RestoreFromTray();
                ::ShowWindow(MainHWND(), SW_RESTORE);
                ::FlashWindow(MainHWND(), FALSE);
            }
            break;

        case WM_NOTIFY:
            Notify(reinterpret_cast<SCNotification *>(lParam));
            break;

        case WM_KEYDOWN:
            return KeyDown(wParam);

        case WM_KEYUP:
            return KeyUp(wParam);

        case WM_SIZE:
            //Platform::DebugPrintf("size %d %x %x\n",iMessage, wParam, lParam);
            if (wParam != 1)
                SizeSubWindows();
            break;

        case WM_MOVE:
            SendEditor(SCI_CALLTIPCANCEL);
            break;

        case WM_GETMINMAXINFO: {
            MINMAXINFO *pmmi = reinterpret_cast<MINMAXINFO *>(lParam);
            if (fullScreen) {
                // Last constants for both x and y are just fiddles - don't know why they are needed
                pmmi->ptMaxSize.x = ::GetSystemMetrics(SM_CXSCREEN) +
                    2 * ::GetSystemMetrics(SM_CXSIZEFRAME) +
                    2;
                pmmi->ptMaxSize.y = ::GetSystemMetrics(SM_CYSCREEN) +
                    ::GetSystemMetrics(SM_CYCAPTION) +
                    ::GetSystemMetrics(SM_CYMENU) +
                    2 * ::GetSystemMetrics(SM_CYSIZEFRAME) +
                    3;
                pmmi->ptMaxTrackSize.x = pmmi->ptMaxSize.x;
                pmmi->ptMaxTrackSize.y = pmmi->ptMaxSize.y;
                return 0;
            } else {
                return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
            }
                               }

        case WM_INITMENU:
            CheckMenus();
            break;

        case WM_CLOSE:
            QuitProgram();
            return 0;

        case WM_QUERYENDSESSION:
            QuitProgram();
            return 1;

        case WM_DESTROY:
            break;

        case WM_SETTINGCHANGE:
            //Platform::DebugPrintf("** Setting Changed\n");
            SendEditor(WM_SETTINGCHANGE, wParam, lParam);
            SendOutput(WM_SETTINGCHANGE, wParam, lParam);
            break;

        case WM_SYSCOLORCHANGE:
            //Platform::DebugPrintf("** Color Changed\n");
            SendEditor(WM_SYSCOLORCHANGE, wParam, lParam);
            SendOutput(WM_SYSCOLORCHANGE, wParam, lParam);
            break;

        case WM_PALETTECHANGED:
            //Platform::DebugPrintf("** Palette Changed\n");
            if (wParam != reinterpret_cast<WPARAM>(MainHWND())) {
                SendEditor(WM_PALETTECHANGED, wParam, lParam);
                //SendOutput(WM_PALETTECHANGED, wParam, lParam);
            }

            break;

        case WM_QUERYNEWPALETTE:
            //Platform::DebugPrintf("** Query palette\n");
            SendEditor(WM_QUERYNEWPALETTE, wParam, lParam);
            //SendOutput(WM_QUERYNEWPALETTE, wParam, lParam);
            return TRUE;

        case WM_ACTIVATEAPP:
            SendEditor(SCI_HIDESELECTION, !wParam);
            // Do not want to display dialog yet as may be in middle of system mouse capture
            ::PostMessage(MainHWND(), WM_COMMAND, IDM_ACTIVATE, wParam);
            break;

        case WM_ACTIVATE:
            //Platform::DebugPrintf("Focus: w:%x l:%x %x e=%x o=%x\n", wParam, lParam, ::GetFocus(), wEditor.GetID(), wOutput.GetID());
            if (wParam != WA_INACTIVE) {
                ::SetFocus(wFocus);
            }
            break;

        case WM_DROPFILES:
            DropFiles(reinterpret_cast<HDROP>(wParam));
            break;

        case WM_COPYDATA:
            return uniqueInstance.CopyData(reinterpret_cast<COPYDATASTRUCT *>(lParam));

        default:
            //Platform::DebugPrintf("default wnd proc %x %d %d\n",iMessage, wParam, lParam);
            return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
        }
        //Platform::DebugPrintf("end wnd proc\n");
    } catch (ScintillaFailure &sf) {
        statusFailure = sf.status;
    }
    if ((statusFailure > 0) && (boxesVisible == 0)) {
        boxesVisible++;
        WCHAR buff[200];
        if (statusFailure == SC_STATUS_BADALLOC) {
            wcscpy(buff, L"Memory exhausted.");
        } else {
            wsprintf(buff, L"Scintilla failed with status %d.", statusFailure);
        }
        wcscat(buff, L" SciTE64 will now close.");
        ::MessageBox(MainHWND(), buff, L"Failure in Scintilla", MB_OK | MB_ICONERROR | MB_APPLMODAL);
        exit(FALSE);
    }
    return 0l;
}

// Take care of 32/64 bit pointers
#ifdef GetWindowLongPtr
static void *PointerFromWindow(HWND hWnd) {
    return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
}
static void SetWindowPointer(HWND hWnd, void *ptr) {
    ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
}
#else
static void *PointerFromWindow(HWND hWnd) {
    return reinterpret_cast<void *>(::GetWindowLong(hWnd, 0));
}
static void SetWindowPointer(HWND hWnd, void *ptr) {
    ::SetWindowLong(hWnd, 0, reinterpret_cast<LONG>(ptr));
}
#endif

LRESULT PASCAL SciTEWin::TWndProc(
                                  HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
                                      //Platform::DebugPrintf("W:%x M:%d WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);

                                      // Find C++ object associated with window.
                                      SciTEWin *scite = reinterpret_cast<SciTEWin *>(PointerFromWindow(hWnd));
                                      // scite will be zero if WM_CREATE not seen yet
                                      if (scite == 0) {
                                          if (iMessage == WM_CREATE) {
                                              LPCREATESTRUCT cs = reinterpret_cast<LPCREATESTRUCT>(lParam);
                                              scite = reinterpret_cast<SciTEWin *>(cs->lpCreateParams);
                                              scite->wSciTE = hWnd;
                                              SetWindowPointer(hWnd, scite);
                                              return scite->WndProc(iMessage, wParam, lParam);
                                          } else
                                              return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
                                      } else
                                          return scite->WndProc(iMessage, wParam, lParam);
}

LRESULT SciTEWin::WndProcI(UINT iMessage, WPARAM wParam, LPARAM lParam) {
    try {
        switch (iMessage) {

    case WM_COMMAND:
        Command(wParam, lParam);
        break;

    case WM_NOTIFY:
        Notify(reinterpret_cast<SCNotification *>(lParam));
        break;

    case WM_PAINT: {
        PAINTSTRUCT ps;
        ::BeginPaint(reinterpret_cast<HWND>(wContent.GetID()), &ps);
        Surface *surfaceWindow = Surface::Allocate();
        if (surfaceWindow) {
            surfaceWindow->Init(ps.hdc, wContent.GetID());
            PRectangle rcPaint(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
            Paint(surfaceWindow, rcPaint);
            surfaceWindow->Release();
            delete surfaceWindow;
        }
        ::EndPaint(reinterpret_cast<HWND>(wContent.GetID()), &ps);
        return 0;
                   }

    case WM_LBUTTONDOWN:
        ptStartDrag = Point::FromLong(lParam);
        capturedMouse = true;
        heightOutputStartDrag = heightOutput;
        ::SetCapture(reinterpret_cast<HWND>(wContent.GetID()));
        //Platform::DebugPrintf("Click %x %x\n", wParam, lParam);
        break;

    case WM_MOUSEMOVE:
        if (capturedMouse) {
            MoveSplit(Point::FromLong(lParam));
        }
        break;

    case WM_LBUTTONUP:
        if (capturedMouse) {
            MoveSplit(Point::FromLong(lParam));
            capturedMouse = false;
            ::ReleaseCapture();
        }
        break;

    case WM_CAPTURECHANGED:
        capturedMouse = false;
        break;

    case WM_SETCURSOR:
        if (ControlIDOfCommand(lParam) == HTCLIENT) {
            Point ptCursor;
            ::GetCursorPos(reinterpret_cast<POINT *>(&ptCursor));
            Point ptClient = ptCursor;
            ::ScreenToClient(MainHWND(), reinterpret_cast<POINT *>(&ptClient));
            if ((ptClient.y > (visHeightTools + visHeightTab)) && (ptClient.y < visHeightTools + visHeightTab + visHeightEditor)) {
                PRectangle rcScintilla = wEditor.GetPosition();
                PRectangle rcOutput = wOutput.GetPosition();
                if (!rcScintilla.Contains(ptCursor) && !rcOutput.Contains(ptCursor)) {
                    wSciTE.SetCursor(splitVertical ? Window::cursorHoriz : Window::cursorVert);
                    return TRUE;
                }
            }
        }
        return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);

    default:
        //Platform::DebugPrintf("default wnd proc %x %d %d\n",iMessage, wParam, lParam);
        return ::DefWindowProc(reinterpret_cast<HWND>(wContent.GetID()),
            iMessage, wParam, lParam);
        }
        //Platform::DebugPrintf("end wnd proc\n");
    } catch (...) {
    }
    return 0l;
}

LRESULT PASCAL SciTEWin::IWndProc(
                                  HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
                                      //Platform::DebugPrintf("W:%x M:%d WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);

                                      // Find C++ object associated with window.
                                      SciTEWin *scite = reinterpret_cast<SciTEWin *>(::PointerFromWindow(hWnd));
                                      // scite will be zero if WM_CREATE not seen yet
                                      if (scite == 0) {
                                          if (iMessage == WM_CREATE) {
                                              LPCREATESTRUCT cs = reinterpret_cast<LPCREATESTRUCT>(lParam);
                                              scite = reinterpret_cast<SciTEWin *>(cs->lpCreateParams);
                                              scite->wContent = hWnd;
                                              SetWindowPointer(hWnd, scite);
                                              return scite->WndProcI(iMessage, wParam, lParam);
                                          } else
                                              return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
                                      } else
                                          return scite->WndProcI(iMessage, wParam, lParam);
}

// from ScintillaWin.cxx
static UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) {
    CHARSETINFO ci = { 0, 0, { { 0, 0, 0, 0 }, { 0, 0 } } };
    BOOL bci = ::TranslateCharsetInfo((DWORD*)characterSet,
        &ci, TCI_SRCCHARSET);

    UINT cp;
    if (bci)
        cp = ci.ciACP;
    else
        cp = documentCodePage;

    CPINFO cpi;
    if (!::IsValidCodePage(cp) && !::GetCPInfo(cp, &cpi))
        cp = CP_ACP;

    return cp;
}

// On NT, convert String from UTF-8 to doc encoding
SString SciTEWin::EncodeString(const SString &s) {
    //::MessageBox(GetFocus(),SString(s).c_str(),"EncodeString:in",0);

    UINT codePage = SendEditor(SCI_GETCODEPAGE);
/*
    if (codePage != SC_CP_UTF8) {
        DWORD charSet = props.GetInt(L"character.set", DEFAULT_CHARSET);
        codePage = CodePageFromCharSet(charSet, codePage);

        int cchWide = ::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s.length(), NULL, 0);
        WCHAR *pszWide = new WCHAR[cchWide + 1];
        ::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s.length(), pszWide, cchWide + 1);

        int cchMulti = ::WideCharToMultiByte(codePage, 0, pszWide, cchWide, NULL, 0, NULL, NULL);
        WCHAR *pszMulti = new WCHAR[cchMulti + 1];
        ::WideCharToMultiByte(codePage, 0, pszWide, cchWide, pszMulti, cchMulti + 1, NULL, NULL);
        pszMulti[cchMulti] = 0;

        SString result(pszMulti);

        delete []pszWide;
        delete []pszMulti;

        //::MessageBox(GetFocus(),result.c_str(),"EncodeString:out",0);
        return result;
    }
*/
    return SciTEBase::EncodeString(s);
}

// On NT, convert String from doc encoding to UTF-8
SString SciTEWin::GetRangeInUIEncoding(Window &win, int selStart, int selEnd) {
    SString s = SciTEBase::GetRangeInUIEncoding(win, selStart, selEnd);

    UINT codePage = SendEditor(SCI_GETCODEPAGE);
/*
    if (codePage != SC_CP_UTF8) {
        DWORD charSet = props.GetInt(L"character.set", DEFAULT_CHARSET);
        codePage = CodePageFromCharSet(charSet, codePage);

        int cchWide = ::MultiByteToWideChar(codePage, 0, s.c_str(), s.length(), NULL, 0);
        wchar_t *pszWide = new wchar_t[cchWide + 1];
        ::MultiByteToWideChar(codePage, 0, s.c_str(), s.length(), pszWide, cchWide + 1);

        int cchMulti = ::WideCharToMultiByte(CP_UTF8, 0, pszWide, cchWide, NULL, 0, NULL, NULL);
        char *pszMulti = new char[cchMulti + 1];
        ::WideCharToMultiByte(CP_UTF8, 0, pszWide, cchWide, pszMulti, cchMulti + 1, NULL, NULL);
        pszMulti[cchMulti] = 0;

        SString result(pszMulti);

        delete []pszWide;
        delete []pszMulti;

        return result;
    }
*/
    return s;
}

int SciTEWin::EventLoop() {
    MSG msg;
    msg.wParam = 0;
    bool going = true;
    while (going) {
        going = ::GetMessage(&msg, NULL, 0, 0);
        if (going) {
            if (!ModelessHandler(&msg)) {
                if (!GetID() ||
                    ::TranslateAccelerator(reinterpret_cast<HWND>(GetID()), GetAcceleratorTable(), &msg) == 0) {
                        ::TranslateMessage(&msg);
                        ::DispatchMessage(&msg);
                }
            }
        }
    }
    return msg.wParam;
}

int PASCAL wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpszCmdLine, int) {

#ifdef NO_EXTENSIONS
    Extension *extender = 0;
#else
    MultiplexExtension multiExtender;
    Extension *extender = &multiExtender;

#ifndef NO_LUA
    SingleThreadExtension luaAdapter(LuaExtension::Instance());
    multiExtender.RegisterExtension(luaAdapter);
#endif

#ifndef NO_FILER
    multiExtender.RegisterExtension(DirectorExtension::Instance());
#endif
#endif
    //Platform::DebugPrintf("Command line is \n%s\n<<", lpszCmdLine);

    SciTEWin::Register(hInstance);
	Scintilla_RegisterClasses(hInstance);
#ifdef STATIC_BUILD

    Scintilla_LinkLexers();
    Scintilla_RegisterClasses(hInstance);
#else
//#ifdef _WIN64 
//    HMODULE hmod = ::LoadLibrary(L"SciLexer64.DLL");
//#else
//    HMODULE hmod = ::LoadLibrary(L"SciLexer.DLL");
//#endif
//    if (hmod == NULL)
//        ::MessageBox(NULL, L"The Scintilla DLL could not be loaded.  SciTE64 will now close", L"Error loading Scintilla", MB_OK | MB_ICONERROR);
#endif

    int result;
    {
        SciTEWin MainWind(extender);
        MainWind.Run(lpszCmdLine);
        result = MainWind.EventLoop();
    }

#ifdef STATIC_BUILD
    Scintilla_ReleaseResources();
#else

//    ::FreeLibrary(hmod);
#endif
	Scintilla_ReleaseResources();

    return result;
}
